import * as vscode from 'vscode';
import { DIAGNOSTIC_IDENT } from './diagnostic';
import globalDictionary from './globalStore/dictionary';
import { DictItem } from './typing';

export function registerCodeActionsProvider(context: vscode.ExtensionContext) {
  if (!vscode.window.activeTextEditor) { return; }

  const selector: vscode.DocumentSelector = { pattern: '**/*' };
  const provider: vscode.CodeActionProvider = {
    provideCodeActions(document, range, context, token) {
      const { diagnostics } = context;

      const synonymInfoMap = gatherSynonymInfoMap(diagnostics);

      const codeActions: vscode.CodeAction[] = [];
      Object.entries(synonymInfoMap).forEach(([code, item]) => {
        item.dictItemSet.forEach(dictItem => {
          dictItem.translatedWords.forEach(word => {
            const codeAction = createCodeActionByReplaceSynonym(document, item.range, code, word);
            codeActions.push(codeAction);
          });
        });
      });

      return codeActions;
    }
  };

  const disposable = vscode.languages.registerCodeActionsProvider(selector, provider);
  context.subscriptions.push(disposable);
}

function createCodeActionByReplaceSynonym(document: vscode.TextDocument, range: vscode.Range, synonymWord: string, unifiedWord: string) {
  let matchCapitalizedUnifiedWord = unifiedWord;
  if (synonymWord.charAt(0) === synonymWord.charAt(0).toUpperCase()) {
    matchCapitalizedUnifiedWord = `${unifiedWord.charAt(0).toUpperCase()}${unifiedWord.slice(1)}`;
  }

  const codeAction = new vscode.CodeAction(
    `Replace "${synonymWord}" to "${matchCapitalizedUnifiedWord}"`,
    vscode.CodeActionKind.QuickFix
  );
  codeAction.command = {
    title: 'Project Dictionary Replace Synonym',
    command: 'projectDictionary.replaceSynonym',
    arguments: [document, range, matchCapitalizedUnifiedWord],
  };

  return codeAction;
}

function gatherSynonymInfoMap(diagnostics: readonly vscode.Diagnostic[]) {
  let synonymInfoMap: ISynonymInfo = {};

  diagnostics.forEach((diagnostic) => {
    const { source, code, range: diagnosticRange } = diagnostic;

    if (source !== DIAGNOSTIC_IDENT) { return; }
    if (!code || typeof code !== 'string') { return; }

    const lowerCaseCode = code.toLowerCase();
    const keys = Object.keys(globalDictionary.dictSynonymMap);
    const codeCorrespondingSynonyms = keys.filter(key => key.toLowerCase() === lowerCaseCode);
    const unFlatDictItems = codeCorrespondingSynonyms.map(synonym => globalDictionary.dictSynonymMap[synonym]);

    unFlatDictItems.flat().forEach(dictItem => {
      if (!synonymInfoMap[code]) {
        synonymInfoMap[code] = {
          range: diagnosticRange,
          dictItemSet: new Set()
        };
      }

      synonymInfoMap[code].dictItemSet.add(dictItem);
    });
  });

  return synonymInfoMap;
}

interface ISynonymInfo {
  [key: string]: {
    range: vscode.Range,
    dictItemSet: Set<DictItem>
  }
}